*  Useful code snippets

** Convert byte vector query string or form data to an alist

The following code is taken from [[https://notabug.org/jbranso/autoassign/src/master/decode.scm]]. Comments have been added and code formatting has been changed. Some code has also been changed, for example some ~define~ expressions have been rewritten into a ~let*~ expression. Semantically though there should be no changes.

#+begin_src scheme
(define-module (decode))


(use-modules
 ;; for match-lambda
 (ice-9 match))
(use-modules
 ;; for utf8->string
 (rnrs bytevectors))
(use-modules
 ;; for append-map
 (srfi srfi-1))
(use-modules
 ;; for cut
 (srfi srfi-26))
(use-modules
 ;; for uri-decode
 (web uri))


(define (acons-list k v alist)
  "Add V to K to alist as list"
  (let ([value (assoc-ref alist k)])
    (if value
        (let ((alist (alist-delete k alist)))
          (acons k (cons v value) alist))
        (acons k (list v) alist))))


(define (list->alist lst)
  "Build a alist of list based on a list of key and values.

   Multiple values can be associated with the same key"
  (let next ([lst lst]
             [out '()])
    (if (null? lst)
        out
        (next (cdr lst) (acons-list (caar lst) (cdar lst) out)))))


(define-public (decode bv)
  "Convert BV querystring or form data to an alist"
  (let* ([string (utf8->string bv)]
         [pairs
          (map
           ;; The procedure cut from srfi-26 returns a procedure, which already
           ;; has some of the arguments specified and still needs to get the
           ;; arguments in positions of the <>. The following will return a
           ;; procedure, which splits strings at an equal sign.
           (cut string-split <> #\=)
           ;; The procedure append-map from srfi-1 is equivalent to first using
           ;; map and then using append to make one list out of all the partial
           ;; lists.
           ;; semi-colon and amp can be used as pair separator in URLs
           (append-map (cut string-split <> #\;)
                       (string-split string #\&)))])
    (list->alist
     ;; When query parameters are given in the URI, they need to be decoded,
     ;; because when they are given, they are, and need to be, URI encoded.
     (map
      ;; The procedure match-lambda returns a procedure, which takes one
      ;; argument and then tries to match the argument with each clause in
      ;; turn. If one clause matches the argument, its corresponding expression
      ;; is evaluated.
      (match-lambda
        ;; If there is a key value pair ...
        [(key value)
         ;; ... uri-decode the key and the value and return those decoded values
         ;; as a pair.
         (cons (uri-decode key) (uri-decode value))])
      ;; Do this for all key value pairs.
      pairs))))
#+end_src
